package factories;

import gui.BackgroundType;
import gui.Tile;

import java.awt.Point;
import java.util.Arrays;
import java.util.Random;

import statics.Constants;

public class RandomWorldGenerator {

	/**
	 * Algemeen
	 */
	private Random rg;
	private int dim_x = Constants.tile_x;
	private int dim_y = Constants.tile_y;
	private ETile[][] e_playfield;
	
	public RandomWorldGenerator(String seed){
		this.rg = new Random(seed.hashCode());
	}
	
	public RandomWorldGenerator(){
		this.rg = new Random();
	}
	
	public boolean initialize(int x, int y){
		dim_x = x;
		dim_y = y;
		return initialize();
	}
	
	public boolean initialize(){
		e_playfield = new ETile[dim_x][dim_y];
		
		for(int x = 0; x < dim_x; x++){
			for(int y = 0; y < dim_y; y++){
				e_playfield[x][y] = new ETile(new Tile(BackgroundType.GRASS));
			}
		}
		
		return true;
	}
	
	public boolean createLayerOne(int chance_on_stone, int scatter){
		System.out.println("Start Layer 1");
		int opp = dim_x * dim_y / 100 * chance_on_stone; //totaal steen oppverlak
		System.out.println("total opp stone: "+opp);
		int maxOpp1stuk = opp / scatter; //Maximale oppvervlakte voor 1 stukje
		System.out.println("Max opp stone: "+maxOpp1stuk);
		//int a = (int) Math.floor((double)opp / (double)maxOpp1stuk); // aantal stukjes
		int a = scatter;
		System.out.println("Amount of Stonepieces: "+a);

		Point[] taken = new Point[a];
		System.out.println("Prepwork end ------\n");
		//Voor elke groep
		for(int i = 0; i < a; i++){		
			System.out.println("Group "+(i+1)+" start:");
			int a2 = rg.nextInt(maxOpp1stuk/2)+(maxOpp1stuk/2);//Random Oppervlakte van 1 stukje
			System.out.println("Opp group: "+a2);
			int z = (int) Math.ceil(Math.sqrt(a2)); //Zijde van die oppervlakte
			System.out.println("Dimension: "+z);
			
			boolean[][] group = new boolean[z][z];
			//Voorbehouden
			Point p = createStoneGroup(taken, z,i);
			if(p != null){ //groep gevonden
				System.out.println("FOUND");
				//Alles op true zetten;
				for(int it = 0; it < group.length; it++) {
				    Arrays.fill(group[it], true);
				}
				//Eroderen
				System.out.println("Starting Erosion step");
				erodeGroup(group,z);
				System.out.println("Starting Adjusting Edges step");
				//Afwerken en plaatsen
				group = adjustEdgesGroup(group,z);
				System.out.println("Starting Placing step");
				
				for(int x = p.x; x < p.x+z+1 && x < dim_x; x++){
					for(int y = p.y; y < p.y+z+1 && y < dim_y; y++){
						if(group[x-p.x][y-p.y]){
							e_playfield[x][y] = new ETile(new Tile(getBackgroundType(group,x-p.x,y-p.y)));
						}else{
							e_playfield[x][y] = new ETile(new Tile(BackgroundType.GRASS));
						}
					}
				}
			}else{
				System.out.println("NOT FOUND");
			}
			System.out.println("End Group --------\n");
		}
		
		return true;		
	}
	
	
	private BackgroundType getBackgroundType(boolean[][] group, int x, int y) {
		boolean t;
		boolean b;
		boolean l;
		boolean r;
		boolean rt;
		boolean rb;
		boolean lt;
		boolean lb;
		//Getting the neighbours
		try{
			t = group[x][y-1];
		}catch(IndexOutOfBoundsException e){
			t = false;
		}
		try{
			b = group[x][y+1];
		}catch(IndexOutOfBoundsException e){
			b = false;
		}
		try{
			l = group[x-1][y];
		}catch(IndexOutOfBoundsException e){
			l = false;
		}
		try{
			r = group[x+1][y];
		}catch(IndexOutOfBoundsException e){
			r = false;
		}
		try{
			lb = group[x-1][y+1];
		}catch(IndexOutOfBoundsException e){
			lb = false;
		}
		try{
			lt = group[x-1][y-1];
		}catch(IndexOutOfBoundsException e){
			lt = false;
		}
		try{
			rb = group[x+1][y+1];
		}catch(IndexOutOfBoundsException e){
			rb = false;
		}
		try{
			rt = group[x+1][y-1];
		}catch(IndexOutOfBoundsException e){
			rt = false;
		}
		
		//Hi neighbours, what am I?
		if(t && !b && r && l)
			return BackgroundType.GRASSTONE_B;
		
		if(t && b && r && l && !lb && lt && rb && rt)
			return BackgroundType.GRASSTONE_ILB;
		
		if(t && b && r && l && lb && !lt && rb && rt)
			return BackgroundType.GRASSTONE_ILT;
		
		if(t && b && r && l && lb && lt && !rb && rt)
			return BackgroundType.GRASSTONE_IRB;
		
		if(t && b && r && l && lb && lt && rb && !rt)
			return BackgroundType.GRASSTONE_IRT;
		
		if(t && b && r && !l)
			return BackgroundType.GRASSTONE_L;
		
		if(t && !b && r && !l)
			return BackgroundType.GRASSTONE_LB;
		
		if(!t && b && r && !l)
			return BackgroundType.GRASSTONE_LT;
		
		if(t && b && !r && l)
			return BackgroundType.GRASSTONE_R;
		
		if(t && !b && !r && l)
			return BackgroundType.GRASSTONE_RB;
		
		if(!t && b && !r && l)
			return BackgroundType.GRASSTONE_RT;
		
		if(!t && b && r && l)
			return BackgroundType.GRASSTONE_T;
		
		return BackgroundType.STONE;
	}

	private boolean[][] adjustEdgesGroup(boolean[][] group, int z) {
		boolean[][] biggergroup = new boolean[z+1][z+1];
		//kopieren van oude in de nieuwe grotere... er moet nix true gezet worden, wnt true is steen....
		for(int x = 0; x < group.length; x++){
			for(int y = 0; y < group[0].length; y++){
				if(group[x][y] == true){
					biggergroup[x][y] = true;
					biggergroup[x+1][y] = true;
					biggergroup[x][y+1] = true;
					biggergroup[x+1][y+1] = true;
				}
			}
		}
		return biggergroup;
	}

	private void erodeGroup(boolean[][] group, int z) {
		for(int x = 0; x < Math.floor(z/2); x++){
			for(int y = 0; y < z; y++){
				if(x==0 || y==0 || y==z-1 || (x>1 && y>0 && y< z-1 && (group[x-1][y] == false || group[x][y-1] == false || group[x][y+1]==false))){
					group[x][y] = succeedChance(30,100);
				}
			}
		}
		for(int x = z-1; x > Math.ceil(z/2); x--){
			for(int y = 0; y < z; y++){
				if(x==z-1 || y==0 || y==z-1 || (x<z-1 && y>0 && y< z-1 && (group[x+1][y] == false || group[x][y-1] == false || group[x][y+1]==false))){
					group[x][y] = succeedChance(30,100);
				}
			}
		}
	}

	private Point createStoneGroup(Point[] taken, int z, int it){
		Point p = null;
		int t = 0;
		do{
			p = getRandomPoint();
			t++;
		}while(!checkAvailability(taken, p, z) && t < 100);//maximaal 20 pogingen doen, randomcontrol
		//Uit de while eerst checken is em available of duurde et gwn te lang een te zoeken?
		if(checkAvailability(taken, p, z)){ //True? dan moet ge em aanmaken eh! :D
			taken[it] = p;
		}
		return p;
	}
	
	private Point getRandomPoint(){
		int x = rg.nextInt(dim_x)+1;
		int y = rg.nextInt(dim_y)+1;
		return new Point(x,y);
	}

	private boolean checkAvailability(Point[] taken, Point p, int z) {
		//HIER VALIDATIE UITVOEREN
		if(taken.length > 0){
			for(Point pt : taken){
				if(pt == null)
					return true;
				
				if (pt.getX() <= p.getX() && p.getX() <= pt.getX()+z+1 && pt.getY() <= p.getY() && p.getY() <= pt.getY()+z+1){
					return false;
				} else if (p.getX() <= pt.getX() && pt.getX() <= p.getX()+z+1 && p.getY() <= pt.getY() && pt.getY() <= p.getY()+z+1){
					return false;
				} else if (p.getX() <= pt.getX()+z+1 && pt.getX()+z+1 <= p.getX()+z && p.getY() <= pt.getY() && pt.getY() <= p.getY()+z+1){
					return false;
				} else if (p.getX() <= pt.getX() && pt.getX() <= p.getX()+z+1 && p.getY() <= pt.getY()+z+1 && pt.getY()+z+1 <= p.getY()+z+1){
					return false;
				}
			}
		}
		return false; //unreachable logic statement
	}

	private boolean succeedChance(int chance, int max_chance){
		int g = rg.nextInt(max_chance)+1;
		return g <= chance;
	}

	public ETile[][] getMap() {
		return e_playfield;
	}
}
